/*
 * File      : drv_usart.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006-2013, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rt-thread.org/license/LICENSE
 *
 * Change Logs:
 * Date           Author       Notes
 * 2009-01-05     Bernard      the first version
 * 2010-03-29     Bernard      remove interrupt Tx and DMA Rx mode
 * 2013-05-13     aozima       update for kehong-lingtai.
 * 2015-01-31     armink       make sure the serial transmit complete in putc()
 * 2016-05-13     armink       add DMA Rx mode
 * 2017-01-19     aubr.cool    add interrupt Tx mode
 * 2017-04-13     aubr.cool    correct Rx parity err
 * 2017-10-20     ZYH          porting to HAL Libraries(with out DMA)
 * 2017-11-15     ZYH          update to 3.0.0
 */
#include "board.h"
#include <rtdevice.h>
#include "drv_usart.h"
#include "main.h"
#include "stm32f1xx_hal.h"

/* STM32 uart driver */
struct stm32_uart
{
    UART_HandleTypeDef *huart;
    IRQn_Type irq;
};

static rt_err_t stm32_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
{
    struct stm32_uart *uart;
    RT_ASSERT(serial != RT_NULL);
    RT_ASSERT(cfg != RT_NULL);
    uart = (struct stm32_uart *)serial->parent.user_data;
    uart->huart->Init.BaudRate   = cfg->baud_rate;
    uart->huart->Init.HwFlowCtl  = UART_HWCONTROL_NONE;
    uart->huart->Init.Mode       = UART_MODE_TX_RX;
    uart->huart->Init.OverSampling = UART_OVERSAMPLING_16;
    switch (cfg->data_bits)
    {
    case DATA_BITS_8:
        uart->huart->Init.WordLength = UART_WORDLENGTH_8B;
        break;
    case DATA_BITS_9:
        uart->huart->Init.WordLength = UART_WORDLENGTH_9B;
        break;
    default:
        uart->huart->Init.WordLength = UART_WORDLENGTH_8B;
        break;
    }
    switch (cfg->stop_bits)
    {
    case STOP_BITS_1:
        uart->huart->Init.StopBits   = UART_STOPBITS_1;
        break;
    case STOP_BITS_2:
        uart->huart->Init.StopBits   = UART_STOPBITS_2;
        break;
    default:
        uart->huart->Init.StopBits   = UART_STOPBITS_1;
        break;
    }
    switch (cfg->parity)
    {
    case PARITY_NONE:
        uart->huart->Init.Parity     = UART_PARITY_NONE;
        break;
    case PARITY_ODD:
        uart->huart->Init.Parity     = UART_PARITY_ODD;
        break;
    case PARITY_EVEN:
        uart->huart->Init.Parity     = UART_PARITY_EVEN;
        break;
    default:
        uart->huart->Init.Parity     = UART_PARITY_NONE;
        break;
    }
    if (HAL_UART_Init(uart->huart) != HAL_OK)
    {
        return RT_ERROR;
    }
    return RT_EOK;
}

static rt_err_t stm32_control(struct rt_serial_device *serial, int cmd, void *arg)
{
    struct stm32_uart *uart;
    RT_ASSERT(serial != RT_NULL);
    uart = (struct stm32_uart *)serial->parent.user_data;
    switch (cmd)
    {
    /* disable interrupt */
    case RT_DEVICE_CTRL_CLR_INT:
        /* disable rx irq */
        NVIC_DisableIRQ(uart->irq);
        /* disable interrupt */
        __HAL_UART_DISABLE_IT(uart->huart, UART_IT_RXNE);
        break;
    /* enable interrupt */
    case RT_DEVICE_CTRL_SET_INT:
        /* enable rx irq */
        NVIC_EnableIRQ(uart->irq);
        /* enable interrupt */
        __HAL_UART_ENABLE_IT(uart->huart, UART_IT_RXNE);
        break;
    }
    return RT_EOK;
}

static int stm32_putc(struct rt_serial_device *serial, char c)
{
    struct stm32_uart *uart;
    RT_ASSERT(serial != RT_NULL);
    uart = (struct stm32_uart *)serial->parent.user_data;
    while (__HAL_UART_GET_FLAG(uart->huart, UART_FLAG_TXE) == RESET);
    uart->huart->Instance->DR = c;
    return 1;
}

static int stm32_getc(struct rt_serial_device *serial)
{
    int ch;
    struct stm32_uart *uart;
    RT_ASSERT(serial != RT_NULL);
    uart = (struct stm32_uart *)serial->parent.user_data;
    ch = -1;
    if (__HAL_UART_GET_FLAG(uart->huart, UART_FLAG_RXNE) != RESET)
    {
        ch = uart->huart->Instance->DR & 0xff;
    }
    return ch;
}

/**
 * Uart common interrupt process. This need add to uart ISR.
 *
 * @param serial serial device
 */
static void uart_isr(struct rt_serial_device *serial)
{
    struct stm32_uart *uart = (struct stm32_uart *) serial->parent.user_data;
    RT_ASSERT(uart != RT_NULL);
    if ((__HAL_UART_GET_FLAG(uart->huart, UART_FLAG_RXNE) != RESET) && (__HAL_UART_GET_IT_SOURCE(uart->huart, UART_IT_RXNE) != RESET))
    {
        rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
        __HAL_UART_CLEAR_FLAG(uart->huart, UART_FLAG_RXNE);
    }
}

static const struct rt_uart_ops stm32_uart_ops =
{
    stm32_configure,
    stm32_control,
    stm32_putc,
    stm32_getc,
};

#if defined(RT_USING_UART1)
/* UART1 device driver structure */
struct stm32_uart uart1;
struct rt_serial_device serial1;

void USART1_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();
    uart_isr(&serial1);
    /* leave interrupt */
    rt_interrupt_leave();

}
#endif /* RT_USING_UART1 */

#if defined(RT_USING_UART2)
/* UART1 device driver structure */
struct stm32_uart uart2;
struct rt_serial_device serial2;

void USART2_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    uart_isr(&serial2);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* RT_USING_UART2 */

#if defined(RT_USING_UART3)
/* UART1 device driver structure */
struct stm32_uart uart3 =
{
    {USART3},
    USART3_IRQn
};
struct rt_serial_device serial3;

void USART3_IRQHandler(void)
{
    /* enter interrupt */
    rt_interrupt_enter();

    uart_isr(&serial3);

    /* leave interrupt */
    rt_interrupt_leave();
}
#endif /* RT_USING_UART3 */



int rt_hw_usart_init(void)
{
    struct stm32_uart *uart;
    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;


#if defined(RT_USING_UART1)
    extern UART_HandleTypeDef huart1;
    uart1.huart=&huart1;
    uart1.irq=USART1_IRQn;
    uart = &uart1;
    config.baud_rate = BAUD_RATE_115200;
    serial1.ops    = &stm32_uart_ops;
    serial1.config = config;
    __HAL_UART_ENABLE_IT(uart->huart, UART_IT_PE);
    __HAL_UART_ENABLE_IT(uart->huart, UART_IT_ERR);
    __HAL_UART_ENABLE_IT(uart->huart, UART_IT_RXNE);
    /* register UART1 device */
    rt_hw_serial_register(&serial1, "uart1",
                          RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
                          uart);
#endif /* RT_USING_UART1 */

    return 0;
}
INIT_BOARD_EXPORT(rt_hw_usart_init);


